﻿using System;
using System.Collections.Generic;

namespace Jayden.Dll.Cards.Poker
{
	public class PokerPot
	{
		private ulong m_Amount;
		private HashSet<string> m_Players = new HashSet<string>();

		private PokerPot m_SidePot;

		public void Add(string player, ulong amount)
		{
			if (amount == 0)
				return;
			if (!m_Players.Contains(player))
			{
				if (Empty)
				{
					m_Players.Add(player);
					m_Amount = amount;
					amount = 0;
				}
				else if (m_Amount > amount)
				{
					PokerPot sidePot = new PokerPot();
					sidePot.m_Players = new HashSet<string>(m_Players);
					sidePot.m_Amount = m_Amount - amount;
					sidePot.m_SidePot = m_SidePot;
					m_SidePot = sidePot;
					m_Amount = amount;
					m_Players.Add(player);
					amount = 0;
				}
				else
				{
					m_Players.Add(player);
					amount -= m_Amount;
				}
			}
			if (amount > 0)
			{
				if (m_SidePot == null)
					m_SidePot = new PokerPot();
				m_SidePot.Add(player, amount);
			}
			Trim();
		}
		public void ReturnUncalledBet(string player, ulong amount)
		{
			if (!m_Players.Contains(player))
				throw new Exception();
			if (m_SidePot != null)
			{
				while (amount > 0 && m_SidePot != null && m_SidePot.m_Players.Count == 1 && m_SidePot.m_Players.Contains(player))
				{
					if (m_SidePot.m_Amount <= amount)
					{
						amount -= m_SidePot.m_Amount;
						m_SidePot = m_SidePot.m_SidePot;
					}
					else
					{
						m_SidePot.m_Amount -= amount;
						amount = 0;
					}
				}
				if (m_SidePot != null && amount > 0)
					m_SidePot.ReturnUncalledBet(player, amount);
				else if (amount > 0)
					throw new Exception();
			}
		}
		public ulong UncalledBet(string player)
		{
			if (m_SidePot == null && m_Players.Contains(player) && m_Players.Count == 1)
				return m_Amount;
			else if (m_SidePot != null)
				return m_SidePot.UncalledBet(player);
			return 0;
		}

		public ulong GetTotalAmountForPlayer(string player)
		{
			if (m_Players.Contains(player))
			{
				return m_Amount + (m_SidePot == null ? 0 : m_SidePot.GetTotalAmountForPlayer(player));
			}
			return 0;
		}
		public ulong GetCallAmountForPlayer(string player)
		{
			ulong missing = 0;
			if (m_SidePot != null)
				missing = m_SidePot.GetCallAmountForPlayer(player);
			if (!m_Players.Contains(player))
				missing += m_Amount;
			return missing;
		}

		public bool Empty { get { return (m_Amount == 0 || m_Players.Count == 0) && (m_SidePot == null || m_SidePot.Empty); } }
		public bool OnlyAnte
		{
			get
			{
				if (Empty)
					return true;
				// this is a false shortcut but it work in the case we are using it
				return m_SidePot == null;
			}
		}
		public ulong TotalAmount
		{
			get
			{
				return m_Amount * (uint)m_Players.Count + (m_SidePot == null ? 0 : m_SidePot.TotalAmount);
			}
		}

		public void Trim()
		{
			if (m_SidePot != null)
			{
				if (m_Players.Count == m_SidePot.m_Players.Count)
				{
					m_Amount += m_SidePot.m_Amount;
					m_SidePot = m_SidePot.m_SidePot;
					Trim();
				}
				else if (m_Players.Count == 0 || m_Amount == 0)
				{
					m_Players = m_SidePot.m_Players;
					m_Amount = m_SidePot.m_Amount;
					m_SidePot = m_SidePot.m_SidePot;
				}
				if (m_SidePot != null)
					m_SidePot.Trim();
			}
		}

		public Dictionary<string, double> GetWinnings(HashSet<string> players)
		{
			Dictionary<string, double> winnings;
			if (m_SidePot != null)
			{
				winnings = m_SidePot.GetWinnings(players);
				Trim();
			}
			else
			{
				winnings = new Dictionary<string, double>();
				foreach (string player in players)
					winnings.Add(player, 0);
			}

			uint count = 0;
			foreach (string player in players)
				if (m_Players.Contains(player))
					count++;

			if (count > 0)
			{
				ulong amount = (ulong)m_Players.Count * m_Amount;
				foreach (string player in players)
					if (m_Players.Contains(player))
						winnings[player] += amount * 1.0 / count;
				m_Players.Clear();
				m_Amount = 0;
			}


			return winnings;
		}

		public override string ToString() { return TotalAmount.ToString(); }
	}
}
